/*
 * http.cpp
 *
 *  Created on: Oct 7, 2013
 *      Author: killerg
 */




#include "http_s.h"
#include "clock.h"
#include "util.h"
#include <strings.h>
#include <string.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
#include <sys/queue.h>
#include <memory>
#include <assert.h>

void generic_handler(struct evhttp_request *req, void *arg)
{
    auto hdlrs = (std::map<std::string, http_server::handle_cb_t>*)arg;

    struct evbuffer *buf;
    buf = evbuffer_new();

    if (buf == NULL) {
        perror("failed to create response buffer");
        assert(0);
    }
    auto rq = std::make_shared<http_server::request>();
    rq->req = req;
    rq->body = "";
    auto input = evhttp_request_get_input_buffer(req);
    while (evbuffer_get_length(input)) {
        int n;
        char cbuf[129];
        n = evbuffer_remove(input, cbuf, sizeof(cbuf));
        cbuf[n] = 0;
        if (n > 0)
            rq->body += cbuf;
    }

    rq->query.clear();

    evkeyvalq kvq;
    auto uri = evhttp_request_get_evhttp_uri(req);
    auto query = evhttp_uri_get_query(uri);
    evhttp_parse_query_str(query, &kvq);
    evkeyval* kv;
    TAILQ_FOREACH(kv, &kvq, next) {
        rq->query[kv->key] = decode(kv->value);
    }
    evhttp_clear_headers(&kvq);

    auto conn = evhttp_request_get_connection(req);
    char* addr = NULL;
    uint16_t port = -1;
    evhttp_connection_get_peer(conn, &addr, &port);
    rq->ip = addr;
    rq->path = evhttp_uri_get_path(uri);

    auto path = evhttp_uri_get_path(uri);
    (*hdlrs)[path](rq);

    evbuffer_free(buf);
}

void http_server::handle(const char* path, handle_cb_t cb) {
    hdlrs[path] = cb;
    evhttp_set_cb(httpd, path, generic_handler, &hdlrs);
}

bool http_server::init(const char* ip, int port) {
    base = NULL;
    httpd = NULL;

    do {
        base = event_base_new();
        if (NULL == base) {
            perror("event_base_new failed");
            break;
        }

        httpd = evhttp_new(base);
        if (NULL == httpd) {
            perror("evhttp_new failed");
            break;
        }

        if (evhttp_bind_socket(httpd, ip, port) < 0) {
            perror("evhttp_new failed");
            break;
        }

        return true;
    } while (0);

    if (httpd != NULL) {
        evhttp_free(httpd);
        httpd = NULL;
    }

    if (base != NULL) {
        event_base_free(base);
        base = NULL;
    }

    return false;
}
void http_server::destroy() {
    if (httpd != NULL) {
        evhttp_free(httpd);
        httpd = NULL;
    }

    if (base != NULL) {
        event_base_free(base);
        base = NULL;
    }
}
void http_server::update() {
    event_base_loop(base, EVLOOP_NONBLOCK);
}

void http_server::request::send_reply(int code, const char* data) {
    evbuffer* output = evbuffer_new();
    evbuffer_add_printf(output, "%s", data);
    evhttp_send_reply(req, code, NULL, output);
    evbuffer_free(output);
}

void http_server::request::send_error(int error) {
    evhttp_send_error(req, error, NULL);
}
